package cz.zcu.fav.ratdroid.service;

import org.joda.time.LocalDateTime;
import org.springframework.http.HttpMethod;

import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;

import cz.zcu.fav.ratdroid.activity.SearchActivity.SearchType;
import cz.zcu.fav.ratdroid.activity.SettingsActivity;
import cz.zcu.fav.ratdroid.bean.Occupations;
import cz.zcu.fav.ratdroid.bean.Objects;
import cz.zcu.fav.ratdroid.bean.Properties;
import cz.zcu.fav.ratdroid.bean.Reservation;
import cz.zcu.fav.ratdroid.bean.Reservations;
import cz.zcu.fav.ratdroid.bean.User;

/**
 * Main service intended to communicate with RAT server via REST
 * 
 * @author Lukas Gemela
 * 
 */
public class RestService
{

	private static RestService service;

	/**
	 * Lazy static instance getter
	 * 
	 * @return restService instance
	 */
	public static RestService getInstance()
	{
		if (service == null)
		{
			service = new RestService();
		}
		return service;
	}

	private RestService()
	{
		// private
	}

	/**
	 * get server properties
	 * 
	 * @param context
	 *            app context
	 * @param listener
	 *            communication listener
	 * @return server properties
	 */
	public RestTask<Properties> loadProperties(Context context,
			IRestListener<Properties> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/session/properties");
		String url = b.build().toString();
		return RestTaskFactory.<Properties> getRestTask(url, context,
				Properties.class, listener, HttpMethod.GET);
	}

	/***
	 * Return object connected to some period back to object pool.
	 * 
	 * @param periodId
	 *            id of period
	 * @param context
	 *            app context
	 * @param listener
	 *            communication listener
	 * @return related reservation with returned period
	 */
	public RestTask<Reservations> returnObject(Integer periodId,
			Context context, IRestListener<Reservations> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/session/reservations/return");
		b.appendQueryParameter("id", periodId + "");
		String url = b.build().toString();

		return RestTaskFactory.<Reservations> getRestTask(url, context,
				Reservations.class, listener, HttpMethod.POST);
	}

	/**
	 * get list of all unreturned periods
	 * 
	 * @param context
	 *            app context
	 * @param listener
	 *            communication listener
	 * @return list of all unreturned periods
	 */
	public RestTask<Reservations> getNotreturnedObjects(Context context,
			IRestListener<Reservations> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/session/reservations/notreturned");
		String url = b.build().toString();
		return RestTaskFactory.<Reservations> getRestTask(url, context,
				Reservations.class, listener, HttpMethod.GET);
	}

	/**
	 * Try to pick up object.
	 * 
	 * @param objectId
	 * @param context
	 *            app context
	 * @param listener
	 *            communication listener
	 * @return picked user reservation or empty list ic case there is no
	 *         reservation which can be picked
	 */
	public RestTask<Reservations> pickUpObject(Integer objectId,
			Context context, IRestListener<Reservations> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/objects/session/object/pickup");
		b.appendQueryParameter("id", objectId + "");
		String url = b.build().toString();

		return RestTaskFactory.<Reservations> getRestTask(url, context,
				Reservations.class, listener, HttpMethod.POST);
	}

	/**
	 * Add new favourite to users favourite list.
	 * 
	 * @param context
	 *            app context
	 * @param listener
	 *            communication listener
	 * @return object as favourite
	 */
	public RestTask<Objects> addFavourite(Context context, Integer objectId,
			IRestListener<Objects> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/session/favourite");
		b.appendQueryParameter("id", objectId + "");
		String url = b.build().toString();
		return RestTaskFactory.<Objects> getRestTask(url, context,
				Objects.class, listener, HttpMethod.POST);

	}

	/***
	 * Remove object from favourite list
	 * 
	 * @param context
	 *            app context
	 * @param objectId
	 *            object id to remove
	 * @param listener
	 *            communication listener
	 * @return updated object
	 */
	public RestTask<Objects> removeFavourite(Context context, Integer objectId,
			IRestListener<Objects> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/session/favourite");
		b.appendQueryParameter("id", objectId + "");
		String url = b.build().toString();
		return RestTaskFactory.<Objects> getRestTask(url, context,
				Objects.class, listener, HttpMethod.DELETE);
	}

	/**
	 * modify period
	 * 
	 * @param context
	 *            app context
	 * @param periodId
	 *            id of edited period
	 * @param valid
	 *            period validity
	 * @param since
	 *            since datetime
	 * @param to
	 *            to datetime
	 * @param listener
	 *            communication listener
	 * @return reservation object with modified period, or reservation which is
	 *         blocking modification
	 */
	public RestTask<Reservations> modifyPeriod(Context context,
			Integer periodId, Boolean valid, LocalDateTime since,
			LocalDateTime to, IRestListener<Reservations> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/session/reservation/period");
		if (since != null && to != null)
		{
			b.appendQueryParameter("since",
					RestUtils.convertJodaTimeToRestTime(since));
			b.appendQueryParameter("to",
					RestUtils.convertJodaTimeToRestTime(to));
		}
		b.appendQueryParameter("id", periodId + "");
		if (valid != null)
		{
			b.appendQueryParameter("valid", String.valueOf(valid));
		}
		String url = b.build().toString();
		return RestTaskFactory.<Reservations> getRestTask(url, context,
				Reservations.class, listener, HttpMethod.POST);
	}

	/***
	 * delete whole reservation
	 * 
	 * @param reservation
	 *            reservation to delete
	 * @param context
	 *            app context
	 * @param listener
	 *            communication listener
	 * @return deleted reservation
	 */
	public RestTask<Reservations> deleteReservation(Reservation reservation,
			Context context, IRestListener<Reservations> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/session/reservation");
		b.appendQueryParameter("id", reservation.getId() + "");
		String url = b.build().toString();

		return RestTaskFactory.<Reservations> getRestTask(url, context,
				Reservations.class, listener, HttpMethod.DELETE);
	}

	/**
	 * get reservations owned by current user
	 * 
	 * @param context
	 * @param since
	 *            since datetime
	 * @param to
	 *            to datetime
	 * @param listener
	 * @return
	 */
	public RestTask<Reservations> getReservationsByUser(Context context,
			LocalDateTime since, LocalDateTime to,
			IRestListener<Reservations> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/session/reservations");
		b.appendQueryParameter("since",
				RestUtils.convertJodaTimeToRestTime(since));
		b.appendQueryParameter("to", RestUtils.convertJodaTimeToRestTime(to));
		String url = b.build().toString();
		return RestTaskFactory.<Reservations> getRestTask(url, context,
				Reservations.class, listener, HttpMethod.GET);
	}

	/**
	 * get reservations related to some object
	 * 
	 * @param context
	 * @param since
	 *            since datetime
	 * @param to
	 *            to datetime
	 * @param listener
	 * @return all reservations in timeslot
	 */
	public RestTask<Reservations> getReservationsByObject(Integer objectId,
			Context context, LocalDateTime since, LocalDateTime to,
			IRestListener<Reservations> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/objects/object/reservations");
		b.appendQueryParameter("since",
				RestUtils.convertJodaTimeToRestTime(since));
		b.appendQueryParameter("to", RestUtils.convertJodaTimeToRestTime(to));
		b.appendQueryParameter("id", objectId + "");
		String url = b.build().toString();
		return RestTaskFactory.<Reservations> getRestTask(url, context,
				Reservations.class, listener, HttpMethod.GET);
	}

	/**
	 * upload new reservation to server
	 * 
	 * @param context
	 * @param listener
	 * @param reservations
	 *            reservation to upload
	 * @return newly created reservation, or reservation which is blocking a new
	 *         one
	 */
	public RestTask<Reservations> addReservation(Context context,
			IRestListener<Reservations> listener, Reservations reservations)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/session/reservation");
		String url = b.build().toString();

		return RestTaskFactory.<Reservations> getRestTask(url, context,
				Reservations.class, listener, HttpMethod.PUT, reservations);
	}

	/**
	 * get all currently logged user favourites
	 * 
	 * @param context
	 *            app context
	 * @param listener
	 *            comm listener
	 * @return favourites
	 */
	public RestTask<Objects> getFavourites(Context context,
			IRestListener<Objects> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/user/favourites");
		String url = b.build().toString();
		return RestTaskFactory.<Objects> getRestTask(url, context,
				Objects.class, listener, HttpMethod.GET);
	}

	private Uri.Builder parseBasics(Context context)
	{
		SharedPreferences pref = PreferenceManager
				.getDefaultSharedPreferences(context);
		String path = pref.getString(SettingsActivity.KEY_PREF_PATH, "");
		int port = Integer.parseInt(pref.getString(
				SettingsActivity.KEY_PREF_PORT, ""));
		String host = pref.getString(SettingsActivity.KEY_PREF_URL, "");

		Uri.Builder b = Uri.parse("https://" + host + ":" + port).buildUpon();
		b.path(path);
		return b;
	}

	/**
	 * get single object by it's identifier
	 * @param context
	 * @param key
	 * @param listener
	 * @return located object
	 */
	public RestTask<Objects> getObjectsByIdentifier(Context context,
			String key, IRestListener<Objects> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/objects/object/identifier");
		b.appendQueryParameter("identifier", key);
		String url = b.build().toString();

		return RestTaskFactory.<Objects> getRestTask(url, context,
				Objects.class, listener, HttpMethod.GET);
	}

	/**
	 * get single object by it's digital id
	 * @param context
	 * @param key
	 * @param listener
	 * @return located object
	 */
	public RestTask<Objects> getObjectsById(Context context, Integer id,
			IRestListener<Objects> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/objects/object/id");
		b.appendQueryParameter("id", id + "");
		String url = b.build().toString();

		return RestTaskFactory.<Objects> getRestTask(url, context,
				Objects.class, listener, HttpMethod.GET);
	}

	/**
	 * get dates occupations by reservations in single month
	 * @param context
	 * @param month month
	 * @param year year
	 * @param listener
	 * @return dates occupation
	 */
	public RestTask<Occupations> getDatesUserOccupation(Context context, int month,
			int year, IRestListener<Occupations> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/users/session/reservations/occupation");
		b.appendQueryParameter("month", String.valueOf(month));
		b.appendQueryParameter("year", String.valueOf(year));
		String url = b.build().toString();

		return RestTaskFactory.<Occupations> getRestTask(url, context, Occupations.class,
				listener, HttpMethod.GET);
	}

	public RestTask<Occupations> getDatesObjectOccupation(Context context, int month,
			int year, IRestListener<Occupations> listener, Integer objectId)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/objects/object/reservations/occupation");
		b.appendQueryParameter("month", String.valueOf(month));
		b.appendQueryParameter("year", String.valueOf(year));
		b.appendQueryParameter("id", objectId + "");
		String url = b.build().toString();
		return RestTaskFactory.<Occupations> getRestTask(url, context, Occupations.class,
				listener, HttpMethod.GET);
	}

	/**
	 * search object 
	 * @param context
	 * @param key string key
	 * @param searchType search type
	 * @param listener
	 * @return list of founded objects
	 */
	public RestTask<Objects> seachObjects(Context context, String key,
			SearchType searchType, IRestListener<Objects> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("/api/rest/objects/object/search");
		b.appendQueryParameter("key", key);
		b.appendQueryParameter("type", searchType.toString());
		String url = b.build().toString();

		return RestTaskFactory.<Objects> getRestTask(url, context,
				Objects.class, listener, HttpMethod.GET);
	}

	/**
	 * get user by it's login name - intended to be used as 
	 * "login procedure"
	 * @param user
	 * @param context
	 * @param listener
	 * @return logged user
	 */
	public RestTask<User> login(String user, Context context,
			IRestListener<User> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("api/rest/users/user/name");
		b.appendQueryParameter("name", user);
		String url = b.build().toString();
		return RestTaskFactory.<User> getRestTask(url, context, User.class,
				listener, HttpMethod.GET);
	}

	/**
	 * get reservation by its id, with all related periods
	 * @param id reservation id
	 * @param context
	 * @param listener
	 * @return reservation with all periods
	 */
	public RestTask<Reservations> getReservation(Integer id, Context context,
			IRestListener<Reservations> listener)
	{
		Uri.Builder b = parseBasics(context);
		b.appendEncodedPath("api/rest/reservations/reservation");
		b.appendQueryParameter("id", id + "");
		String url = b.build().toString();
		return RestTaskFactory.<Reservations> getRestTask(url, context,
				Reservations.class, listener, HttpMethod.GET);
	}

}
